package database

import (
	"strings"
	"strconv"
)

// define table interface
type TableInterface interface {
	Init(table string)
	Gets() ([]map[string]string, error)
	Get() (map[string]string, error)
	Lists(cols string) ([]map[string]string, error)
	List(col string) (map[string]string, error)
	Count() (int64, error)
	Insert(data map[string]interface {}) (int64, error)
	Update(data map[string]interface {}) (int64, error)
	Delete() (int64, error)
}

// db default table struct
type Table struct {
	db *Db
	tableName string
	sqlBuilder *Sql
	sqlArgs   []interface {}
}

// init table object with table_name and db object
func (this *Table) Init(table string, db*Db) *Table {
	this.tableName = table
	this.db = db
	return this
}

// reset sql builder in table object
func (this *Table) resetSql() {
	this.sqlBuilder = NewSql(this.tableName, "*")
	this.sqlArgs = make([]interface {}, 0)
}

// set where condition, allow placeholder params
func (this *Table) Where(str string, args... interface {}) *Table {
	this.sqlBuilder.Where(str)
	if len(args) > 0 {
		for _, arg := range args {
			this.sqlArgs = append(this.sqlArgs, arg)
		}
	}
	return this
}

// set order condition, not allow placeholder params
func (this *Table) Order(str string) *Table {
	this.sqlBuilder.Order(str)
	return this
}

// set limit condition, not allow placeholder params
func (this *Table) Limit(num int) *Table {
	this.sqlBuilder.Limit(num)
	return this
}

// set pager division, not allow placeholder params
func (this *Table) Page(page int, size int) *Table {
	this.sqlBuilder.Page(page, size)
	return this
}

// gets all data
func (this *Table) Gets() ([]map[string]string, error) {
	sql := this.sqlBuilder.Select()
	args := this.sqlArgs
	this.resetSql()
	return this.db.Query(sql, args...)
}

// get one data
func (this *Table) Get() (map[string]string, error) {
	sql := this.sqlBuilder.Select()
	args := this.sqlArgs
	this.resetSql()
	return this.db.One(sql, args...)
}

// get all data with custom columns
func (this *Table) Lists(col string) ([]map[string]string, error) {
	this.sqlBuilder.Column = col
	return this.Gets()
}

// get data with custom columns
func (this *Table) List(col string) (map[string]string, error) {
	this.sqlBuilder.Column = col
	return this.Get()
}

// get count number
func (this *Table) Count() (int64, error) {
	sql := this.sqlBuilder.Count()
	args := this.sqlArgs
	res, e := this.db.One(sql, args...)
	this.resetSql()
	if e != nil {
		return -1, e
	}
	return strconv.ParseInt(res["countNum"], 10, 64)
}

// insert data, return last id
func (this *Table) Insert(data map[string]interface {}) (int64, error) {
	cols := make([]string, 0)
	args := make([]interface {}, 0)
	for k, v := range data {
		cols = append(cols, k)
		args = append(args, v)
	}
	if len(this.sqlArgs) > 0 {
		for _, arg := range this.sqlArgs {
			args = append(args, arg)
		}
	}
	this.sqlBuilder.Column = strings.Join(cols, ",")
	sql := this.sqlBuilder.Insert()
	this.resetSql()
	return this.db.Exec(sql, args...)
}

// update data, return affected rows
func (this *Table) Update(data map[string]interface {}) (int64, error) {
	cols := make([]string, 0)
	args := make([]interface {}, 0)
	for k, v := range data {
		cols = append(cols, k)
		args = append(args, v)
	}
	if len(this.sqlArgs) > 0 {
		for _, arg := range this.sqlArgs {
			args = append(args, arg)
		}
	}
	this.sqlBuilder.Column = strings.Join(cols, ",")
	sql := this.sqlBuilder.Update()
	this.resetSql()
	return this.db.Exec(sql, args...)
}

// delete data, return affected rows
func (this *Table) Delete() (int64, error) {
	sql := this.sqlBuilder.Delete()
	args := this.sqlArgs
	this.resetSql()
	return this.db.Exec(sql, args...)
}

// create new table with table_name and db connection
func NewTable(table string, db *Db) *Table {
	t := &Table{}
	t.Init(table, db)
	t.resetSql()
	return t
}
